﻿using System;
using System.Collections.Generic;
using System.Text;
using FlexminAPI;
using System.Xml;
using FlexLib.FileTree;
using FlexLib.RegistryTree;
using System.Windows.Forms;
using System.IO;

namespace FlexLib.Plugins
{
	public class XmlPluginSerialization
	{
		public static PluginAssemblyPaths ParseDllPath(XmlNode node)
		{
			string masterPath = null;
			string slavePath = null;

			foreach (XmlNode settingNode in node.ChildNodes)
			{
				switch (settingNode.Name)
				{
					case "admin":
						masterPath = settingNode.InnerText;
						break;
					case "user":
						slavePath = settingNode.InnerText;
						break; 
				}
			}

			return new PluginAssemblyPaths(masterPath, slavePath);
		}

		public static PluginInfo ParseInfo(XmlNode node)
		{
			string name = null;
			string displayName = null;
			Version version = null;
			bool resident = false;
			bool multihost = false;
			string description = null;

			foreach (XmlNode settingNode in node.ChildNodes)
			{
				switch (settingNode.Name)
				{
					case "name":
						name = settingNode.InnerText;
						break;
					case "displayname":
						displayName = settingNode.InnerText;
						break;
					case "version":
						version = new Version(settingNode.InnerText);
						break;
					case "description":
						description = settingNode.InnerText;
						break;
					case "multihost":
						multihost = settingNode.InnerText == "true";
						break;
					case "resident":
						resident = settingNode.InnerText == "true";
						break;
				}
			}

			if (name == null ||
				displayName == null ||
				description == null ||
				version == null)
			{
				return null;
			}

			return new PluginInfo(name, displayName, description, version, resident, multihost);
		}

		private static string TrimPath(string path, string project_path)
		{
			if (path.Contains(project_path))
				path = path.Substring(project_path.Length + 1);
			return path;
		}

		private static void WriteNodes(XmlTextWriter xmlwriter, FileTreeNode node, string savePath)
		{
			try
			{
				foreach (FileTreeNode subNode in node.SubNodes)
				{
					xmlwriter.WriteStartElement("folder");
					xmlwriter.WriteAttributeString("name", subNode.Name);
					try
					{
						foreach (string file in subNode.Files)
						{
							xmlwriter.WriteStartElement("file");
							xmlwriter.WriteString(TrimPath(file, savePath));
							xmlwriter.WriteEndElement();
						}
					}
					catch { }
					finally
					{
						WriteNodes(xmlwriter, subNode, savePath);
						xmlwriter.WriteEndElement();
					}
				}
			}
			catch { }
		}

		private static void WriteNodes(XmlTextWriter xmlwriter, RegTreeKey key)
		{
			try
			{
				foreach (RegTreeKey subKey in key.SubKeys)
				{
					xmlwriter.WriteStartElement("key");
					xmlwriter.WriteAttributeString("name", subKey.Name);
					try
					{
						foreach (RegProperty property in subKey.Properties)
						{
							try
							{
								xmlwriter.WriteStartElement("property");
								xmlwriter.WriteAttributeString("name", property.Name);
								xmlwriter.WriteAttributeString("type", property.Type.ToString());
								xmlwriter.WriteAttributeString("value", property.Value);
							}
							catch { }
							finally { xmlwriter.WriteEndElement(); }
						}
					}
					catch { }
					finally
					{
						WriteNodes(xmlwriter, subKey);
						xmlwriter.WriteEndElement();
					}
				}
			}
			catch { }
		}
		
		public static void GenerateFprojFile(XmlTextWriter xmlwriter, InstPackage package, string savePath)
		{
			xmlwriter.Formatting = Formatting.Indented;
			xmlwriter.WriteStartDocument();
			
			xmlwriter.WriteStartElement("plugin");
			
			xmlwriter.WriteStartElement("user");
			xmlwriter.WriteStartElement("files");
			WriteNodes(xmlwriter, package.RootNodeUser, savePath);
			xmlwriter.WriteEndElement();
			xmlwriter.WriteStartElement("registry");
			WriteNodes(xmlwriter, package.RootKeyUser);
			xmlwriter.WriteEndElement();
			xmlwriter.WriteEndElement();

			xmlwriter.WriteStartElement("admin");
			xmlwriter.WriteStartElement("files");
			WriteNodes(xmlwriter, package.RootNodeAdmin, savePath);
			xmlwriter.WriteEndElement();
			xmlwriter.WriteStartElement("registry");
			WriteNodes(xmlwriter, package.RootKeyAdmin);
			xmlwriter.WriteEndElement();
			xmlwriter.WriteEndElement();

			SerializeDlls(xmlwriter, package.AdminDllPath, package.UserDllPath, savePath);
			SerializePluginInfo(xmlwriter, package.ToPluginInfo());

			xmlwriter.WriteStartElement("icon");
			try
			{
				xmlwriter.WriteString(package.Icon);
			}
			catch
			{
				xmlwriter.WriteString("");
			}
			xmlwriter.WriteEndElement();

			xmlwriter.WriteEndElement();
			
			xmlwriter.WriteEndDocument();
			xmlwriter.Close();
		}

		private static Dictionary<string, string> AdminDllFiles = new Dictionary<string, string>();
		private static Dictionary<string, string> UserDllFiles = new Dictionary<string, string>();

		private static void FillNode(FileTreeNode fileNode, XmlNode xmlNode, ModuleType type, string projPath)
		 {
			foreach (XmlNode xmlSubNode in xmlNode.ChildNodes)
			{
				switch (xmlSubNode.Name)
				{
					case "folder":
						FileTreeNode fileSubNode = fileNode.AddNode(xmlSubNode.Attributes["name"].Value);
						if (xmlSubNode.HasChildNodes)
							FillNode(fileSubNode, xmlSubNode, type, projPath);
						break;
					case "file":
						if (Path.GetExtension(xmlSubNode.InnerText).ToLower() == ".dll")
							if (type == ModuleType.Admin)
								AdminDllFiles.Add(fileNode.GetNonRootedPath() + '\\' + Path.GetFileName(xmlSubNode.InnerText), xmlSubNode.InnerText);
							else
								UserDllFiles.Add(fileNode.GetNonRootedPath() + '\\' + Path.GetFileName(xmlSubNode.InnerText), xmlSubNode.InnerText);

						if (Path.IsPathRooted(xmlSubNode.InnerText))
							fileNode.AddFile(xmlSubNode.InnerText);
						else
							fileNode.AddFile(projPath + Path.DirectorySeparatorChar + xmlSubNode.InnerText);
						break;
				}
			}
		}

		private enum ModuleType
		{
			Admin,
			User
		}

		private static void FillNode(RegTreeKey key, XmlNode xmlNode)
		{
			foreach (XmlNode xmlSubNode in xmlNode.ChildNodes)
			{
				switch (xmlSubNode.Name)
				{
					case "key":
						RegTreeKey subKey = key.AddKey(xmlSubNode.Attributes["name"].Value);
						if (xmlSubNode.HasChildNodes)
							FillNode(subKey, xmlSubNode);							
						break;
					case "property":
						key.AddProperty(new RegProperty(xmlSubNode.Attributes["name"].Value, xmlSubNode.Attributes["value"].Value, RegProperty.GetTypeByName(xmlSubNode.Attributes["type"].Value)));
						break;
				}
			}
		}

		public static InstPackage GetPackageFromFproj(string filename)
		{
			AdminDllFiles.Clear();
			UserDllFiles.Clear();

			InstPackage package = new InstPackage();

			XmlDocument document = new XmlDocument();
			document.Load(filename);

			XmlNode node = document.SelectSingleNode("/plugin/user/files");
			FillNode(package.RootNodeUser, node, ModuleType.User, Path.GetDirectoryName(filename));
			node = document.SelectSingleNode("/plugin/user/registry");
			FillNode(package.RootKeyUser, node);
			
			node = document.SelectSingleNode("/plugin/admin/files");
			FillNode(package.RootNodeAdmin, node, ModuleType.Admin, Path.GetDirectoryName(filename));
			node = document.SelectSingleNode("/plugin/admin/registry");
			FillNode(package.RootKeyAdmin, node);

			node = document.SelectSingleNode("/plugin/dll/admin");
			if (node != null)
				package.AdminDllPath = node.InnerText;

			node = document.SelectSingleNode("/plugin/dll/user");
			if (node != null)
				package.UserDllPath = node.InnerText;

			node = document.SelectSingleNode("/plugin/info/name");
			if (node != null)
				package.ModuleName = node.InnerText;

			node = document.SelectSingleNode("/plugin/info/displayname");
			if (node != null)
				package.DisplayName = node.InnerText;		

			node = document.SelectSingleNode("/plugin/icon");
			if (node != null)
				package.Icon = node.InnerText;
			
			node = document.SelectSingleNode("/plugin/info/description");
			if (node != null)
				package.Description = node.InnerText;

			node = document.SelectSingleNode("/plugin/info/multihost");
			if (node != null)
			{
				if (node.InnerText.ToLower() == "true")
					package.IsMultihost = true;
				else
					package.IsMultihost = false;
			}

			node = document.SelectSingleNode("/plugin/info/resident");
			if (node != null)
			{
				if (node.InnerText.ToLower() == "true")
					package.IsResident = true;
				else
					package.IsResident = false;
			}
			try
			{
				package.AdminDllFiles = AdminDllFiles;
				package.UserDllFiles = UserDllFiles;
			}
			catch { }

			return package;
		}

		public static void GenerateModuleInfoXML(Stream outputStrem, PluginInfo info, string AdminDllPath, string UserDllPath, string savePath)
		{
			XmlTextWriter xmlwriter = null;
			xmlwriter = new XmlTextWriter(outputStrem, Encoding.UTF8);
			xmlwriter.Formatting = Formatting.Indented;

			xmlwriter.WriteStartDocument();
			xmlwriter.WriteStartElement("FlexminModule");

			SerializeDlls(xmlwriter, AdminDllPath, UserDllPath, savePath);
			SerializePluginInfo(xmlwriter, info);

			xmlwriter.WriteEndElement();
			xmlwriter.WriteEndDocument();
			xmlwriter.Close();
		}

		public static void SerializeDlls(XmlTextWriter writer, string AdminDllPath, string UserDllPath, string savePath)
		{
			writer.WriteStartElement("dll");
			writer.WriteStartElement("admin");
			writer.WriteString(AdminDllPath);
			writer.WriteEndElement();
			writer.WriteStartElement("user");
			writer.WriteString(UserDllPath);
			writer.WriteEndElement();
			writer.WriteEndElement();
		}

		public static void SerializePluginInfo(XmlTextWriter writer, PluginInfo info)
		{
			writer.WriteStartElement("info");
			writer.WriteStartElement("name");
			writer.WriteString(info.Name);
			writer.WriteEndElement();
			writer.WriteStartElement("displayname");
			writer.WriteString(info.DisplayName);
			writer.WriteEndElement();
			writer.WriteStartElement("version");
			try
			{
				writer.WriteString(info.PluginVersion.ToString());
			}
			catch
			{
				writer.WriteString("");
			}
			writer.WriteEndElement();
			writer.WriteStartElement("description");
			writer.WriteString(info.Description);
			writer.WriteEndElement();
			writer.WriteStartElement("multihost");
			writer.WriteString(info.Multihost.ToString().ToLower());
			writer.WriteEndElement();
			writer.WriteStartElement("resident");
			writer.WriteString(info.Resident.ToString().ToLower());
			writer.WriteEndElement();
			writer.WriteEndElement();
		}
	}
}
